//包含头文件
#include<sys/socket.h>
#include <sys/types.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <netinet/in.h>

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>

#define PORT 3333   //端口号
#define ADMIN_PASSWORD "iot2"		//密码

struct SharedMemoryData {
    int messageCount;  // 记录当前共享内存中消息的数量
    struct Message {
        char name[20];  // 存储发送消息的用户姓名
        char time[30];  // 存储消息发送时间
        char content[1024];  // 存储消息内容
    } messages[10];  // 假设最多存储10条群聊消息，可根据实际调整大小
};

struct message
{
    long type;
    char buf[1024];
}mybuf;

//定义用户结构体，用户名，密码，套接字描述符
struct User{                              
	char username[20];
	char userpwd[20];
	int sockfd;
	int state;//在线状态
	//...
};

//默认用户
struct User users[100] = {                          
	
};

int userlen = 0;//已注册用户的个数
int shmid;
// 全局变量，指向共享内存地址的指针
struct SharedMemoryData *shmData;
// 用于标记共享内存写入线程是否结束的变量
int writeThreadEnd = 0;
// 用于标记共享内存读取线程是否结束的变量
int readThreadEnd = 0;
// 用于标记共享内存读取线程是否阻塞的变量，初始化为1表示阻塞
int readThreadBlocked = 1;
// 用于存储上次读取的消息内容，方便比较是否有新消息
char lastReadMessage[1024] = "";

//函数声明
void writefile(void);
void readfile(void);
void writehistory(char *msg, char fileFlag);
void readhistory(char **msg, char fileFlag);
void addUser(struct User user);
int findUser(char name[]);
struct User returnUser(char name[]);
void updateUser(char name[20],struct User user);
void deleteUser(char name[]);
void getCurrentTime(char *timeBuffer);
void *readSharedMemory(void *arg);


void getCurrentTime(char *timeBuffer)
{
    time_t currentTime;
    struct tm *timeInfo;

    currentTime = time(NULL);
    timeInfo = localtime(&currentTime);

    // 格式化为 "YYYY-MM-DD HH:MM:SS" 的字符串
    strftime(timeBuffer, 30, "%Y-%m-%d %H:%M:%S", timeInfo);
}
void menu()//捕获信号用于显示菜单          
{
	char password[10];
	system("clear");

	printf("\n");
	printf("                  ◢▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄◣  \n");
	printf("                  ※        ******                ******      ※\n");
	printf("                  ※      ******◆   赛 博 蜂 巢   ◆ ******    ※\n");
	printf("                  ※        ******                ******      ※\n");
	printf("                  ※==========================================※\n");
	printf("                  ※    ******        ******        ******    ※\n");
	printf("                  ※   *      *      *      *      *      *   ※\n");
	printf("                  ※***        ******        ******        ***※\n");
	printf("                  ※   *      *      *      *      *      *   ※\n");
	printf("                  ※    ******        ******        ******    ※\n");
	printf("                  ※   *      *      *      *      *      *   ※\n");
	printf("                  ※***        ******        ******        ***※\n");
	printf("                  ※   *      *      *      *      *      *   ※\n");
	printf("                  ※    ******        ******        ******    ※\n");
	printf("                  ※==========================================※\n");
	printf("                  ※                                          ※\n");
	printf("                  ※ ◆◆◆◆  欢迎使用linux聊天室服务端系统 ◆◆◆◆ ※\n");
	printf("                  ※                                          ※\n");
	printf("                  ※▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄※  \n");
	printf("\n");

	printf("请输入管理员密码以进入菜单: ");
	while (1) 
	{
		scanf("%s", password);
		if (strcmp(password, ADMIN_PASSWORD) == 0) 
		{
			printf("============   管 理 员 登 录 成 功 ， 即 将 进 入 服 务 端 管 理   ============\n");
			break;
		} else 
		{
			printf("密码错误，请重新输入:");
		}
	}
	sleep(1);
	system("clear");
	printf("                  ◢▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄◣  \n");
	printf("                  ※                                          ※\n");
	printf("                  ※        ******                ******      ※\n");
	printf("                  ※      ******◆   赛 博 蜂 巢   ◆ ******    ※\n");
	printf("                  ※        ******                ******      ※\n");
	printf("                  ※==========================================※\n");
	printf("                  ※ 1 显示所有用户信息 ****        ******    ※\n");
	printf("                  ※ 2 删除指定用户信息     *      *      *   ※\n");
	printf("                  ※ 3 添加指定用户信息      ******        ***※\n");
	printf("                  ※ 4 查看在线用户  *      *      *      *   ※\n");
	printf("                  ※ 5 强制用户下线   ******        ******    ※\n");
	printf("                  ※ 6 查看聊天记录  *      *      *      *   ※\n");
	printf("                  ※ 7 退出服务器 ***        ******        ***※\n");
	printf("                  ※==========================================※\n");
	printf("                  ※                                          ※\n");
	printf("                  ※▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄※  \n");
	printf("请选择\n");


}


//------------------------客户端功能实现函数-------------------------------------
char* login(int sockfd)    //验证登录 
{
	char buf[20];
	static char name[20];
	int n,m;
	while(1)
	{
		recv(sockfd,name,20,0);//接收用户名   //参数分别为：描述符，接收缓存数据拷贝到的位置，name的长度，一般默认0       作用：将对于接口接收缓存区的数据拷贝到name，返回值为实际接收数据长度
		n = findUser(name);//记录接收到的用户名在用户数组中的位置
		if(n==-1)
		{
			send(sockfd,"-1",strlen("-1")+1,0);
			continue;
		}

		send(sockfd,"0",strlen("0")+1,0);
		
		while(1)
		{
			recv(sockfd,buf,20,0);//接收密码
			if(strcmp(buf,users[n].userpwd)!=0)
			{
				send(sockfd,"-1",strlen("-1")+1,0);
				continue;
			}
			send(sockfd,"0",strlen("-1")+1,0);
			users[n].state = 1;
			users[n].sockfd = sockfd;//登录成功后，将登录者的套接字描述符赋给登录用户的.sockfd
			return name;
		}	
		
		
	}
}
char* Regist(int sockfd)    //验证注册  
{
	char buf[20];
	static char name[20];
	int n,m;
	struct User user;
	while(1)
	{
		recv(sockfd,name,20,0);//接收用户名   //参数分别为：描述符，接收缓存数据拷贝到的位置，name的长度，一般默认0       作用：将对于接口接收缓存区的数据拷贝到name，返回值为实际接收数据长度
		strcpy(user.username,name);		
		n = findUser(name);//记录接收到的用户名在用户数组中的位置
		if(n==-1)//用户不存在（可注册）
		{
			send(sockfd,"-1",strlen("-1")+1,0);
			while(1)
		   {
			recv(sockfd,buf,20,0);//接收密码
			strcpy(user.userpwd,buf);	
			users[n].sockfd = sockfd;
			addUser(user);
			return name;
		   }	
			
			
		}else
		{
			send(sockfd,"0",strlen("0")+1,0);//用户存在不可注册
			continue;
		}	
		
	}
}

void change(int sockfd,char name[20])//客户端修改密码
{
	char c[20];
	struct User user;
	char buf[1024];
	char n[20];
	user = returnUser(name);//将该用户的所有信息获取到

	
	sprintf(buf,"是否更改用户名为%s用户的密码  y 是   n 否?\n",name);
	usleep(50000);
	send(sockfd,buf,strlen(buf)+1,0);
	
	
	while(1)
	{
		recv(sockfd,c,20,0);
		if(c[0]=='y' || c[0]=='Y')
			{
				send(sockfd,"请输入新密码:\n",strlen("请输入新密码:\n")+1,0);
				
				while(1)
				{
				recv(sockfd,n,20,0);
				strcpy(user.userpwd,n);
				updateUser(name,user);
				return;
				}
				
			}
			else
			{
				return;
			}
	}	

}
void showUserallc(int sockfd)//将所有用户发给客户端显示
{
	int i;
	char buf[1024];
	for( i = 0; i < userlen;i++)
		{
			usleep(50000);
			sprintf(buf,"%s\t%s\t%d\t%d",users[i].username,users[i].userpwd,users[i].sockfd,users[i].state);
			
			send(sockfd,buf,strlen(buf)+1,0);
		    
		}
		printf("全部好友已查询完毕\n");
		usleep(50000);
	    send(sockfd,"end",strlen("end")+1,0);//发送完毕时，向服务端发送结束表示	
		return;

}


void onlineUserc(int sockfd)//将在线用户发给客户端显示
{
	int i;
	char buf[1024];
	for( i = 0; i < userlen;i++)
		{
			
			if(users[i].state == 1)//找到出在线用户
			{
				usleep(50000);
				sprintf(buf,"%s\t%s\t%d\t%d",users[i].username,users[i].userpwd,users[i].sockfd,users[i].state);
			    send(sockfd,buf,strlen(buf)+1,0);		        
			}
			
		}
		printf("在线好友已查询完毕\n");
		usleep(30000);
	    send(sockfd,"end",strlen("end")+1,0);//发送完毕时，向服务端发送结束表示	
		return;
}




//----------------------------------------------------------------------------------------------------------------
//该函数专门处理已经连接客户端的请求
void *fun1(int *csockfd)
{
	char buf[1024];
	char buf1[1024];
	int n;
	char a[20];
	char buf0[1024];

	char name[20];
	int msgid;
	msgid = msgget((key_t)60,IPC_CREAT | 0666);//创建消息队列  参数：键值（电脑看），权限
	//处理注册与登录
	while(1)
	{
		recv(*csockfd,buf,20,0); //客户端发送的套接字，接收的数据，长度，标志
		switch (buf[0])
		{
		case '1':
			{
				
				Regist(*csockfd);
				break;
			}
		case '2':
			{
				int d;
				strcpy(name,login(*csockfd));
				while(1)
				{

				
					// recv(*csockfd,a,20,0);
					if(recv(*csockfd,a,20,0)<=0)
					{
						close(*csockfd);
						return;
					}
					send(*csockfd,"keep",strlen("keep")+1,0);
					
					switch (a[0])
					{
					case '1'://聊天
						{
							//登录成功之后可以开始聊天
							while(1)
							{
								if(recv(*csockfd,buf,20,0)<=0)
								{
									close(*csockfd);
									return;
								}
								if(!strncmp(buf,"end",3))  //接收到end时退出循环
								{
									
									send(*csockfd,"end",strlen("end")+1,0);//告诉客户端可以退出聊天功能
									// printf("...........\n");
							        // while(1);
									break;
								}
								if(buf[0]=='$')
								{
									send(*csockfd,"进入群聊",strlen("进入群聊")+1,0);
								}
								else if(buf[0]=='#')
								{
									printf("%s登录群聊\n",name);
									printf("群聊服务器已开启\n");
									sprintf(mybuf.buf,"%s登录群聊",name);
									mybuf.type=1;
									if((msgsnd(msgid,(void *)&mybuf,1024,0))<0)    //发送消息队列   队列id，将接收到的内容存到buf，0（接收第一个消息），0（阻塞）   没消息时阻塞
									{
										perror("msgnd");
									}
											
									// send(*csockfd,buf,strlen(buf)+1,0);
									// usleep(50000);
									// send(*csockfd,"群聊服务器已开启",strlen("群聊服务器已开启")+1,0);
									while(1)
									{
									
											// recv(*csockfd,buf1,1024,0);
											if(recv(*csockfd,buf1,1024,0)<=0)
											{
												close(*csockfd);
												return;
											}
											usleep(10000);
											if(!strncmp(buf1,"end",3))  //接收到end时退出循环
											{
												// send(*csockfd,"keep",strlen("keep")+1,0);
												break;
											}
											// printf("%s:%s\n",name,buf1);
											sprintf(mybuf.buf,"%s的群发消息:%s",name,buf1);
											mybuf.type=1;
											if((msgsnd(msgid,(void *)&mybuf,1024,0))<0)    //发送消息队列   队列id，将接收到的内容存到buf，0（接收第一个消息），0（阻塞）   没消息时阻塞
											{
												perror("msgnd");
											}
											
									
									}
									
								}
								if(buf[0]=='@')
								{
									send(*csockfd,"请输入你想要聊天的用户名",strlen("请输入你想要聊天的用户名")+1,0);
									usleep(10000);
									while(1)
									{
										recv(*csockfd,buf,20,0);
										usleep(10000);
										n = findUser(buf);
										if(n==-1)
										{
											send(*csockfd,"该用户名不存在，请重新输入你想聊天人的用户名",strlen("该用户名不存在，请重新输入你想聊天人的用户名")+1,0);
											continue;
										}
										if(users[n].state==0)
										{
											send(*csockfd,"该用户名未登录，请重新输入你想聊天人的用户名",strlen("该用户名未登录，请重新输入你想聊天人的用户名")+1,0);
											continue;
										}
										send(*csockfd,"用户连接成功，请发送您消息内容：",strlen("用户连接成功，请发送您的消息内容：")+1,0);
										strcpy(buf1,buf);
										while(1)
										{
											char buf_time[30];
											// recv(*csockfd,buf,1024,0);
											if(recv(*csockfd,buf,1024,0)<=0)
											{
												close(*csockfd);
												return;
											}
											usleep(10000);
											if(!strncmp(buf,"end",3))  
											{
												// send(*csockfd,"keep",strlen("keep")+1,0);
												break;
											}
											getCurrentTime(buf_time);
											sprintf(buf0,"%s 来自用户%s的访问(%s):%s",buf_time,name,buf1,buf);//将发送者：发送的内容拷贝到buf0
											send(users[n].sockfd,buf0,strlen(buf0)+1,0);//发送给之前输入的用户
											writehistory(buf0, '0');
										}
										break;
									}
									
								}

							}

							break;
						}
						case '2':	//修改密码
						{
							change(*csockfd,name);
							break;
						}
						case '3'://显示所有好友给客户端
						{
							showUserallc(*csockfd);
							break;
						}
						case '4'://显示在线好友给客户端
						{
							onlineUserc(*csockfd);
							break;
						}
						case '5':
						{
							d=2;
							break;
						}
						case '6':
						{
							d=1;
							break;
						}
						
					
					default:
						{
							break;
						}
						
					}
					if(d==1)//实现退出后的处理工作
					{
						d=0;
						int i = findUser(name);
						users[i].state = 0;
						users[i].sockfd = 0;
						break;
					}
					if(d==2)//注销后退出
					{
						d=0;
						deleteUser(name);
						break;
					}
				
                }
				break;
			}
		default:
			{
				break;
			}
		}
	}

		
	
}
//------------------------菜单功能实现函数---------------------------------------
int findUser(char name[])//传入用户名，返回该用户在数组中的位置，找到返回数组位置找不到返回-1；     //传入参数用户名
{
	int i;
	for(i=0;i<userlen;i++)
	{
		if(strcmp(users[i].username,name)==0)//找到用户
		{
			return i;//返回用户所在位置
		}
	}
	return -1;//没有找到
}
struct User returnUser(char name[])	//找到并返回结构体
{
	int i;
	for(i=0;i<userlen;i++)
	{
		if(strcmp(users[i].username,name)==0)//找到用户
		{
			return users[i];//返回用户结构体
		}
	}
	
}
void updateUser(char name[20],struct User user)//找到并更新结构体
{
	int i;
	for(i=0;i<userlen;i++)
	{
		if(strcmp(users[i].username,name)==0)//找到用户
		{
			users[i] = user;//数据替换成最新的数据
			writefile();
			return;
		}
	}
	
}
void showUserall()//显示所有用户,需美化
{
	int i;
	for( i = 0; i < userlen;i++)
		{
			printf("%s\t%s\t%d\t%d\n",users[i].username,users[i].userpwd,users[i].sockfd,users[i].state);
		}
		return;

}
void addUser(struct User user)//添加用户（注册）
{
	strcpy(users[userlen].username,user.username);
	strcpy(users[userlen].userpwd,user.userpwd);
	userlen++;
	writefile();//写入文件
}
void deleteUser(char name[])//删除指定用户信息
{
	int i,j;
	for(i = 0; i < userlen; i++)
	{
		if(strcmp(users[i].username,name) == 0)//找到数据位置，将后面的数组向前移动1位
		{
			for(j = i; j < userlen-1; j++)
			{
				users[j] = users[j+1];
			}
			
			userlen--;
		}
	}
	writefile();//写入文件
}

void readfile(void)//从文件读取数据传入数组
{
	FILE *fp;//文件指针是一种特殊的指针，用于指向打开的文件
	int n;
	struct User user;//局部结构体变量
	
	fp= fopen("user_data.txt","r+b");// 以二进制形式只读打开文件
	if(!fp)//使用 fopen 函数打开一个文件时，它会返回一个文件指针。指针为空时新建文件
	{
		fp = fopen("user_data.txt","w+b");//以二进制形式读写打开或建立文件
	}
	while(1)
	{
		n = fread(&user,sizeof(struct User),1,fp);//将读取的字节的数据，存储到 user 变量（局部）中。
		if(n!=1)//如果读取成功，则 n 的值为 1；如果读取到文件末尾，则 n 的值为 0。
		{
			return;
		}
		else
		{
			addUser(user);
		}
	}
	fclose(fp);
}

//根据传入的 fileFlag 参数来确定要打开的文件名（'0' 对应 chat_history.txt(私聊)，'1' 对应 group_chat_history.txt（群聊）），
void readhistory(char **msg, char fileFlag)
{
    FILE *fp;
    char *filename;
    char line[1024];  // 假设一行聊天记录最长不超过1024字节，可根据实际调整大小
    size_t totalSize = 0;  // 用于记录已分配内存的总大小
    size_t currentSize = 0;  // 当前已使用的内存大小

    if (fileFlag == '0')
    {
        filename = "chat_history.txt";
    }
    else if (fileFlag == '1')
    {
        filename = "group_chat_history.txt";
    }
    else
    {
        // 处理错误情况，这里简单地打印错误信息并返回，你可以根据实际需求进行更完善的错误处理
        printf("无效的文件标识参数\n");
        return;
    }

    fp = fopen(filename, "r");  // 以文本只读模式打开文件（因为要按行读取文本内容）
    if (!fp)
    {
        // 文件不存在等打开失败情况的处理，这里简单打印错误信息并返回，可根据实际需求调整，比如可以创建文件等操作
        perror("打开文件失败");
        return;
    }

    // 初始分配一定大小的内存
    *msg = (char *)malloc(1024);
    if (*msg == NULL)
    {
        perror("内存分配失败");
        fclose(fp);
        return;
    }
    totalSize = 1024;
    currentSize = 0;

    while (fgets(line, sizeof(line), fp)!= NULL)  // 逐行读取文件内容，直到文件末尾
    {
        size_t lineLength = strlen(line);
        if (currentSize + lineLength + 1 > totalSize)  // 判断当前内存是否足够存储新读取的一行内容（+1是为了预留'\0'结束符的空间）
        {
            // 如果不够，动态扩容内存
            totalSize *= 2;  // 简单示例，每次扩容为原来的2倍，可根据实际情况调整策略
            char *newMsg = (char *)realloc(*msg, totalSize);
            if (newMsg == NULL)
            {
                perror("内存重新分配失败");
                free(*msg);
                fclose(fp);
                return;
            }
            *msg = newMsg;
        }
        strcpy(*msg + currentSize, line);  // 将读取到的一行内容复制到已分配内存的合适位置
        currentSize += lineLength;
    }
    (*msg)[currentSize] = '\0';  // 添加字符串结束符

    fclose(fp);
}


void writefile(void)//将用户数据写入文件
{
	int i = 0;
	FILE *fp;
	
	fp = fopen("user_data.txt","w+b");//以二进制形式读写打开或建立文件
	for(i=0;i<userlen;i++)
	{
		fwrite(&users[i],sizeof(struct User),1,fp);//将结构体变量users写入文件
	}
	fclose(fp);
}

//根据传入的 fileFlag 参数来确定要打开的文件名（'0' 对应 chat_history.txt(私聊)，'1' 对应 group_chat_history.txt（群聊）），
void writehistory(char *msg, char fileFlag)
{
    FILE *fp;
    char *filename;
    char *line = strtok(msg, "\n");  // 以换行符为分隔符分割字符串，获取第一行内容

    if (fileFlag == '0')
    {
        filename = "chat_history.txt";
    }
    else if (fileFlag == '1')
    {
        filename = "group_chat_history.txt";
    }
    else
    {
        // 处理错误情况，这里简单地打印错误信息并返回，你可以根据实际需求进行更完善的错误处理
        printf("无效的文件标识参数\n");
        return;
    }

    fp = fopen(filename, "a");  // 以文本追加模式打开文件，用于逐行追加写入内容
    if (fp == NULL)
    {
        perror("打开文件失败");
        return;
    }

    while (line!= NULL)
    {
        // 写入一行内容（包含换行符）
        fputs(line, fp);
        fputc('\n', fp);

        line = strtok(NULL, "\n");  // 获取下一行内容
    }

    fclose(fp);
}
void txx() //踢下线  
{  
    char name[20];
	char c;
	printf("请输入踢出的用户名:\n");
	while ((c = getchar()) != '\n' && c != EOF);
	scanf("%s",name);
    int i = findUser(name);
    if (i == -1) {
        // 用户不存在
		printf("用户不存在\n");
		printf("踢出失败，请按下回车键继续\n");
		// while ((c = getchar()) != '\n' && c != EOF);
		getchar(); // 捕获一个字符
        return;
    }
    // 关闭 socket
	send(users[i].sockfd,"您将在三秒钟后被踢出",strlen("您将在三秒钟后被踢出")+1,0);
	printf("正在踢出...\n");
	sleep(2);
	send(users[i].sockfd,"您已被踢出，请输入任意值退出",strlen("您已被踢出，请输入任意值退出")+1,0);
	if(users[i].state!=0)
	{ 
		users[i].state = 0;
		close(users[i].sockfd);
		users[i].sockfd = 0;
		sleep(1);
		printf("踢出成功，请按下回车键继续\n");
		// while ((c = getchar()) != '\n' && c != EOF);
		getchar(); // 捕获一个字符
	}else{
		printf("用户未登录\n");
		printf("踢出失败，请按下回车键继续\n");
		// while ((c = getchar()) != '\n' && c != EOF);
		getchar(); // 捕获一个字符
	}
}

void onlineUser()//在线用户
{
	int i;
	for( i = 0; i < userlen;i++)
		{
			if(users[i].state == 1)//找到出在线用户
			{
                printf("%s\t%s\t%d\t%d\n",users[i].username,users[i].userpwd,users[i].sockfd,users[i].state);
			}
			
		}
		return;
}



//--------------------------------------------------------------------------
void *fun2()
{
	int n,d,x;
	char c;
	
	while(1)
	{
		
	    scanf("%d",&n);

		switch (n)
		{
		case 1:
			{
				system("clear");
				printf("*=====================================*\n");
				printf("用户名\t密码\tsockfd\t在线状态\n");
				showUserall();//显示所有用户
				printf("*=====================================*\n");
				while(1)
				{
				
				
				while ((c = getchar()) != '\n' && c != EOF);//清空输入缓冲区，避免scanf函数跳过的问题。
				printf("请输入t返回菜单\n");
				scanf("%c",&c);
				if(c=='t')
				{
					system("clear");
					break;
				}else{
					printf("无效输入\n");
				}
				}
				break;
			}

		case 2:     //删除用户信息
			{
				
				int m;
				char name[20];
				char c;

				while(1)
				{
					system("clear");
					printf("请输入您要删除的用户名\n");
					while ((c = getchar()) != '\n' && c != EOF);
					scanf("%s",name);

					m = findUser(name);
					if(m==-1)
					{
						printf("您输入的用户名不存在，请重新输入，请按下回车键继续\n");
						// while ((c = getchar()) != '\n' && c != EOF);
						getchar(); // 捕获一个字符
						break;
					}

					printf("是否确定删除用户名为%s用户的信息\n    y 是   n 否?\n",name);
					while ((c = getchar()) != '\n' && c != EOF);
					scanf("%c",&c);

					if(c=='y' || c=='Y')
					{
						//删除用户
						deleteUser(name);
						printf("删除成功，请按下回车键返回菜单\n");
						x=1;
						break;
					}
					else
					{
						system("clear");
						break;
					}
	            }
				 break;
			}
		case 3: //添加指定用户信息
			{
				char name[20];
				int m;
				struct User user;
				while(1)
				{
					
					system("clear");
					printf("请输入添加用户的用户名\n");
					while ((c = getchar()) != '\n' && c != EOF);
					scanf("%s",name);

					m = findUser(name);
					if(m!=-1)
					{
						printf("您输入的用户名已经存在，请重新输入，请按下回车键继续\n");
						// while ((c = getchar()) != '\n' && c != EOF);
						getchar();
						break;
					}

					strcpy(user.username,name);
					strcpy(user.userpwd,"123456");


					//增加用户信息
					addUser(user);
					printf("增加成功！(默认密码:123456) \n\n");

					printf("是否继续添加用户信息\n    y 是   n 否\n");
					while ((c = getchar()) != '\n' && c != EOF);
					scanf("%c",&c);
					if(c=='y' || c=='Y')
					{
						system("clear");
						continue;
					}
					else
					{
						
						break;
					}

				}
				break;
			}
		case 4://查看在线用户
			{
				system("clear");
				printf("*=====================================*\n");
				printf("用户名\t密码\tsockfd\t在线状态\n");
				onlineUser();//显示所有用户
				printf("*=====================================*\n");
				while(1)
				{
				while ((c = getchar()) != '\n' && c != EOF);//清空输入缓冲区，避免scanf函数跳过的问题。
				printf("请输入t返回菜单\n");
				scanf("%c",&c);
				if(c=='t')
				{
					system("clear");
					break;
				}else{
					printf("无效输入\n");
				}
				}
				break;
			}
		case 5://踢指定用户下线
			{
				
				while(1)
				{
					system("clear");
					txx();
					break;
				}
				
				break;
			}
		case 6://查看记录
		     {
				char inputChoice[10];  // 定义一个字符数组来接收用户输入的字符串，假设最长输入不超过10个字符，可根据实际调整大小
				char *buffer = NULL;  // 初始化为空指针，等待动态分配内存

				printf("提示:end退出\n");
				printf("1----查看私聊记录\n");
				printf("2----查看群聊记录\n");

				while (1)
				{
					printf("请输入你的选择: ");
					scanf("%9s", inputChoice);  // 读取用户输入的字符串，限制最多读取9个字符（预留一个字节给字符串结束符'\0'）

					if (strcmp(inputChoice, "1") == 0)
					{
						// 查看私聊记录
						readhistory(&buffer, '0');
						if (buffer!= NULL && strlen(buffer) > 0)
						{
							printf("私聊记录：\n");
							char *line = buffer;  // 从缓冲区起始位置开始处理
							int lineNumber = 1;
							while (*line!= '\0')
							{
								// 找到换行符位置
								char *nextLine = strchr(line, '\n');
								if (nextLine!= NULL)
								{
									*nextLine = '\0';  // 暂时将换行符替换为字符串结束符，方便打印当前行
									printf("%d. %s\n", lineNumber++, line);
									*nextLine = '\n';  // 恢复换行符
									line = nextLine + 1;  // 指向下一行起始位置
								}
								else
								{
									// 如果没有找到换行符，说明已经到达缓冲区末尾，直接打印剩余内容
									printf("%d. %s\n", lineNumber++, line);
									break;
								}
							}
						}
						else
						{
							printf("暂无私聊记录\n");
						}
						free(buffer);  // 释放动态分配的内存
						buffer = NULL;  // 重置指针为 NULL
					}
					else if (strcmp(inputChoice, "2") == 0)
					{
						// 查看群聊记录
						readhistory(&buffer, '1');
						if (buffer!= NULL && strlen(buffer) > 0)
						{
							printf("群聊记录：\n");
							char *line = buffer;  // 从缓冲区起始位置开始处理
							int lineNumber = 1;
							while (*line!= '\0')
							{
								// 找到换行符位置
								char *nextLine = strchr(line, '\n');
								if (nextLine!= NULL)
								{
									*nextLine = '\0';  // 暂时将换行符替换为字符串结束符，方便打印当前行
									printf("%d. %s\n", lineNumber++, line);
									*nextLine = '\n';  // 恢复换行符
									line = nextLine + 1;  // 指向下一行起始位置
								}
								else
								{
									// 如果没有找到换行符，说明已经到达缓冲区末尾，直接打印剩余内容
									printf("%d. %s\n", lineNumber++, line);
									break;
								}
							}
						}
						else
						{
							printf("暂无群聊记录\n");
						}
						free(buffer);  // 释放动态分配的内存
						buffer = NULL;  // 重置指针为 NULL
					}
					else if (strcmp(inputChoice, "end") == 0)
					{
						break;
					}
					else
					{
						printf("无效的选择，请重新输入\n");
					}
				}

				break;
		     }
		case 7://退出服务器
		     {
				
	            printf("正在退出服务器...\n");
				printf("已成功退出\n");
				exit(0);
		     }
		default:
			{
				d=1;
				break;
			}
		}
		while ((c = getchar()) != '\n' && c != EOF);
		system("clear");
		system("clear");
		printf("                  ◢▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄◣  \n");
		printf("                  ※                                          ※\n");
		printf("                  ※        ******                ******      ※\n");
		printf("                  ※      ******◆   赛 博 蜂 巢   ◆ ******    ※\n");
		printf("                  ※        ******                ******      ※\n");
		printf("                  ※==========================================※\n");
		printf("                  ※ 1 显示所有用户信息 ****        ******    ※\n");
		printf("                  ※ 2 删除指定用户信息     *      *      *   ※\n");
		printf("                  ※ 3 添加指定用户信息      ******        ***※\n");
		printf("                  ※ 4 查看在线用户  *      *      *      *   ※\n");
		printf("                  ※ 5 强制用户下线   ******        ******    ※\n");
		printf("                  ※ 6 查看聊天记录  *      *      *      *   ※\n");
		printf("                  ※ 7 退出服务器 ***        ******        ***※\n");
		printf("                  ※==========================================※\n");
		printf("                  ※                                          ※\n");
		printf("                  ※▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄※  \n");
		printf("请选择\n");
		if(d==1){
            printf("输入有误，请重新输入\n");
			d=0;
		}
		if(x==1){
            printf("删除成功\n");
			x=0;
		}
		
	}
		
}
void *readSharedMemory(void *arg)
{
    // printf("********共享内存读取线程已启动****************\n");
    while (1)
    {
        // 检查共享内存中是否有消息
        if (shmData->messageCount > 0)
        {
            // 比较当前messages[0]与上次接收的消息是否不同
            if (strcmp(shmData->messages[0].content, lastReadMessage)!= 0)
            {
                char msgToWrite[1024];
                // 格式化要写入的消息字符串
                sprintf(msgToWrite, "%s %s 在群聊说：%s", shmData->messages[0].time, shmData->messages[0].name, shmData->messages[0].content);
                // 写入群聊记录
                writehistory(msgToWrite, '1');
                // 更新上次接收的消息为当前messages[0]
                strcpy(lastReadMessage, shmData->messages[0].content);
            }
        }
        // 这里可以添加一个机制，比如等待一段时间（使用例如sleep或者usleep等函数）后再重新检查共享内存，避免过度占用CPU资源
        // 示例等待1秒后再次检查共享内存是否有新消息
        usleep(10000);
    }
    // printf("********共享内存读取线程结束****************\n");
    // 分离共享内存，如果分离失败，打印错误信息

    return NULL;
}

int main(int argc,char *argv[])
{
	readfile();
	menu();

	int ret;
	char e;
	int sockfd,csockfd;
	struct sockaddr_in s,c;//结构体系统头文件（<sys/socket.h>）已经定义，用来获取ip和端口
	pthread_t readThread,id,id2;     //线程id
	int *temp;
	//int len = 1;
	socklen_t len = 1;
    
	shmid = shmget((key_t)1234, sizeof(struct SharedMemoryData), IPC_CREAT | 0666);  // 使用一个合适的键值（这里示例用1234，实际可按规则选取），设置权限等创建共享内存段
    if (shmid == -1)
    {
        perror("shmget创建共享内存失败");
        return 1;
    }
    // 将共享内存段连接到进程的地址空间
    void *temp_shmat = shmat(shmid, NULL, 0);
    if (temp_shmat == (void *)-1)
    {
        perror("shmat连接共享内存到进程空间失败");
        return 1;
    }
    shmData = (struct SharedMemoryData *)temp_shmat;
    shmData->messageCount = 0;  // 初始化消息数量为0


	//1 创建socket                                        //网络编程中的I/O口（套接字），作用与网络中的其他应用程序进行通信，一般为IP地址和端口的组合
	sockfd = socket(AF_INET,SOCK_STREAM,0);  //ipv4,tcp（字符串流），补0    返回值为套接字描述符，出错-1(scokfd)

	//2 绑定
	//2.1 准备ip结构体
	s.sin_family = AF_INET;//地址的协议族ipv4
	s.sin_port = PORT;//ip端口号，数据传输时不可以直接通过ip地址，需要用端口号访问   IP有0-65535个端口号
	s.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址，INADDR_ANY获取当前ip地址
	bzero(&(s.sin_zero),8);//通过函数给其他补0（8位），可省略

	//2.2 绑定
	bind(sockfd,(struct sockaddr*)&s,sizeof(struct sockaddr));//套接字叙述符， 本地IP，地址长度通过sizeof计算                          将创建出的socker号与当前的ip以及端口进行绑定

	//3 监听
	listen(sockfd,20);  //监听哪一个socket,最大请求个数    作用：准备等待客户端连接，需要告诉它最大支持连接多少个客户端
	pthread_create(&id2,0,(void *)fun2,NULL);//使用多线程，因为主函数一直在等待客户端的连接，所以需使用其他线程处理菜单功能
	int readRet = pthread_create(&readThread, 0, (void *)readSharedMemory, NULL);
	if (readRet!= 0)
	{
		perror("创建共享内存读取线程失败");
	}

	while(1)//主线程一直等待客户端的连接
	{

		//4 等待链接
		csockfd = accept(sockfd,(struct sockaddr*)&c,&len);//有一个socket传递过来，接收到客户端的ip和端口，客户端ip的长度       //返回的是一个客户端套接字（不是描述符），发送和接收要用此socket

		//5 如果有客户端进行连接，则创建子线程与其对应
		temp = (int *)malloc(4);//给temp分配4个int指针类型的空间
		*temp = csockfd;//使temp所指的空间存入客户端传来的套接字
		pthread_create(&id,0,(void *)fun1,(void *)temp);//参数:线程id，线程属性0，线程函数（传参和返回都为void*），给线程函数传的参数（传指针类型）  并列同时执行，使用同一个进程资源（mian也为一个线程）

		//5 发送数据
		//send(csockfd,buf,strlen(buf)+1,0);
	}

	//6 关闭
	printf("服务器已退出\n");
	printf("请按下回车键退出\n");
	while ((e = getchar()) != '\n' && e != EOF);
	getchar(); // 捕获一个字符
	ret = pthread_join(readThread, NULL);
    if (ret!= 0)
    {
        perror("线程回收失败");
        return 1;
    }
	if (shmdt((void *)shmData) == -1)
    {
        perror("shmdt");
    }
	// 撤销共享内存
	if (shmctl(shmid, IPC_RMID, 0) < 0)
	{
		perror("shmctl撤销共享内存失败");
	}
	
	close(csockfd);
	close(sockfd);
}

